
#include "sock-inet-tcp.hxx"

#include <netinet/in.h>
#include <sys/socket.h>

using std::move;
using std::runtime_error;

////////////////////////////////////////////////////////////////////////////////

sock_inet_tcp::sock_inet_tcp()
: sock_inet(SOCK_STREAM, IPPROTO_TCP) {
    if((file = fdopen(sfd, "a+")) == NULL)
    {
        throw runtime_error("fdopen");
    }
}

sock_inet_tcp::sock_inet_tcp(int sfd)
: sock_inet(sfd) {
    if((file = fdopen(sfd, "a+")) == NULL)
    {
        throw runtime_error("fdopen");
    }
}

sock_inet_tcp::sock_inet_tcp(sock_inet_tcp&& rhs)
: sock_inet(move(rhs)), file(rhs.file) {
    rhs.file = NULL;
}

sock_inet_tcp::~sock_inet_tcp() {
    if(file != NULL) {
        fclose(file);
    }
}

sock_inet::address sock_inet_tcp::get_remote() const {
    sock_inet::address addr(0, 0);
    socklen_t addrlen = sizeof(sockaddr);

    if(getpeername(sfd, (sockaddr*)&addr.get_sockaddr(), &addrlen) < 0) {
        throw std::runtime_error("getpeername"); // TODO: specific exception
    }

    return addr;
}

FILE* sock_inet_tcp::get_file() const {
    return file;
}

sock_inet_tcp sock_inet_tcp::accept() {
    sock_inet_tcp s(::accept(sfd, NULL, NULL));

    if(s.sfd < 0) {
        throw std::runtime_error("accept"); // TODO: specific exception
    }

    return s;
}

sock_inet_tcp sock_inet_tcp::listen(const sock_inet::address& a) {
    sock_inet_tcp s;

    s.bind(a);

    if(::listen(s.sfd, 1) < 0) {
        throw std::runtime_error("listen"); // TODO: specific exception
    }

    return s;
}

sock_inet_tcp sock_inet_tcp::connect(const sock_inet::address& a) {
    sock_inet_tcp s;

    if(::connect(s.sfd, &a.get_sockaddr(), (socklen_t)sizeof(sockaddr)) < 0) {
        throw std::runtime_error("connect"); // TODO: specific exception
    }

    return s;
}
